{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 86,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pythoncom\n",
    "import win32con\n",
    "import win32com.client as win32"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### What is a IDispatch Object?\n",
    "Exposes objects, methods and properties to programming tools and other applications that support Automation. COM components implement the IDispatch interface to enable access by Automation clients, such as Visual Basic.\n",
    "\n",
    "- **GetIDsOfNames** - Maps a single member and an optional set of argument names to a corresponding set of integer DISPIDs, which can be used on subsequent calls to Invoke.\n",
    "- **GetTypeInfo** - Retrieves the type information for an object, which can then be used to get the type information for an interface.\n",
    "- **GetTypeInfoCount** - Retrieves the number of type information interfaces that an object provides (either 0 or 1).\n",
    "- **Invoke** - Provides access to properties and methods exposed by an object."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 195,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "PyIDispatch"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "win32com.client.dynamic.CDispatch"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# using Win32 we did the following\n",
    "xlApp = win32.dynamic.Dispatch('Excel.Application')\n",
    "\n",
    "# create an instance of the Excel App\n",
    "xlCom = pythoncom.connect('Excel.Application')\n",
    "\n",
    "# we will see later but these are very similiar objects they may appear different but they're created the same.\n",
    "display(type(xlCom))\n",
    "display(type(xlApp))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 192,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on PyIDispatch object:\n",
      "\n",
      "class PyIDispatch(PyIUnknown)\n",
      " |  Define the behavior of a PythonCOM Interface type.\n",
      " |  \n",
      " |  Method resolution order:\n",
      " |      PyIDispatch\n",
      " |      PyIUnknown\n",
      " |      interface-type\n",
      " |      object\n",
      " |  \n",
      " |  Methods defined here:\n",
      " |  \n",
      " |  GetIDsOfNames(...)\n",
      " |  \n",
      " |  GetTypeInfo(...)\n",
      " |  \n",
      " |  GetTypeInfoCount(...)\n",
      " |  \n",
      " |  Invoke(...)\n",
      " |  \n",
      " |  InvokeTypes(...)\n",
      " |  \n",
      " |  __delattr__(self, name, /)\n",
      " |      Implement delattr(self, name).\n",
      " |  \n",
      " |  __eq__(self, value, /)\n",
      " |      Return self==value.\n",
      " |  \n",
      " |  __ge__(self, value, /)\n",
      " |      Return self>=value.\n",
      " |  \n",
      " |  __getattribute__(self, name, /)\n",
      " |      Return getattr(self, name).\n",
      " |  \n",
      " |  __gt__(self, value, /)\n",
      " |      Return self>value.\n",
      " |  \n",
      " |  __le__(self, value, /)\n",
      " |      Return self<=value.\n",
      " |  \n",
      " |  __lt__(self, value, /)\n",
      " |      Return self<value.\n",
      " |  \n",
      " |  __ne__(self, value, /)\n",
      " |      Return self!=value.\n",
      " |  \n",
      " |  __repr__(self, /)\n",
      " |      Return repr(self).\n",
      " |  \n",
      " |  __setattr__(self, name, value, /)\n",
      " |      Implement setattr(self, name, value).\n",
      " |  \n",
      " |  ----------------------------------------------------------------------\n",
      " |  Data and other attributes defined here:\n",
      " |  \n",
      " |  __hash__ = None\n",
      " |  \n",
      " |  ----------------------------------------------------------------------\n",
      " |  Methods inherited from PyIUnknown:\n",
      " |  \n",
      " |  QueryInterface(...)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# let's take a look at our PyIDispatch Object.\n",
    "help(xlCom)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# GetIDsOfNames\n",
    "\n",
    "- Link to Win32Com Documentation: http://timgolden.me.uk/pywin32-docs/PyIDispatch__GetIDsOfNames_meth.html\n",
    "- Link to Microsoft Documentation: https://docs.microsoft.com/en-us/windows/desktop/api/oaidl/nf-oaidl-idispatch-getidsofnames\n",
    "\n",
    "(int, ...)/int = GetIDsOfNames(name)\n",
    "\n",
    "Get the DISPID for the passed names.\n",
    "\n",
    "Parameters:\n",
    "- name : string\n",
    "- desc : A name to query for\n",
    "\n",
    "Comments:\n",
    "Currently the LCID can not be specified, and LOCALE_SYSTEM_DEFAULT is used.\n",
    "\n",
    "Return Value:\n",
    "If the first parameter is a sequence, the result will be a tuple of integers for each name in the sequence. If the first parameter is a single string, the result is a single integer with the ID of requested item."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 208,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "572"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "302"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# we can get the ID of a method or property\n",
    "\n",
    "# let's grab the workbook property\n",
    "workbooks_id = xlCom.GetIDsOfNames('Workbooks')\n",
    "display(workbooks_id)\n",
    "\n",
    "# let's grab the Quit method.\n",
    "quit_id = xlCom.GetIDsOfNames('Quit')\n",
    "display(quit_id)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# GetTypeInfoCount\n",
    "\n",
    "- `expression.GetTypeInfoCount()`\n",
    "    - Where expression is a PyIDispatchObject\n",
    "- Number of Arguments: 0\n",
    "- Return type: int\n",
    "\n",
    "Retrieves the number of PyITypeInfos the object provides."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 213,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 213,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Grab the Type Info Count\n",
    "myTypeInfo = xlCom.GetTypeInfoCount()\n",
    "\n",
    "# this means we have one PyITypeInfos Objects.\n",
    "myTypeInfo"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 196,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on PyITypeInfo object:\n",
      "\n",
      "class PyITypeInfo(PyIUnknown)\n",
      " |  Define the behavior of a PythonCOM Interface type.\n",
      " |  \n",
      " |  Method resolution order:\n",
      " |      PyITypeInfo\n",
      " |      PyIUnknown\n",
      " |      interface-type\n",
      " |      object\n",
      " |  \n",
      " |  Methods defined here:\n",
      " |  \n",
      " |  GetContainingTypeLib(...)\n",
      " |  \n",
      " |  GetDocumentation(...)\n",
      " |  \n",
      " |  GetFuncDesc(...)\n",
      " |  \n",
      " |  GetIDsOfNames(...)\n",
      " |  \n",
      " |  GetImplTypeFlags(...)\n",
      " |  \n",
      " |  GetNames(...)\n",
      " |  \n",
      " |  GetRefTypeInfo(...)\n",
      " |  \n",
      " |  GetRefTypeOfImplType(...)\n",
      " |  \n",
      " |  GetTypeAttr(...)\n",
      " |  \n",
      " |  GetTypeComp(...)\n",
      " |  \n",
      " |  GetVarDesc(...)\n",
      " |  \n",
      " |  __delattr__(self, name, /)\n",
      " |      Implement delattr(self, name).\n",
      " |  \n",
      " |  __eq__(self, value, /)\n",
      " |      Return self==value.\n",
      " |  \n",
      " |  __ge__(self, value, /)\n",
      " |      Return self>=value.\n",
      " |  \n",
      " |  __getattribute__(self, name, /)\n",
      " |      Return getattr(self, name).\n",
      " |  \n",
      " |  __gt__(self, value, /)\n",
      " |      Return self>value.\n",
      " |  \n",
      " |  __le__(self, value, /)\n",
      " |      Return self<=value.\n",
      " |  \n",
      " |  __lt__(self, value, /)\n",
      " |      Return self<value.\n",
      " |  \n",
      " |  __ne__(self, value, /)\n",
      " |      Return self!=value.\n",
      " |  \n",
      " |  __repr__(self, /)\n",
      " |      Return repr(self).\n",
      " |  \n",
      " |  __setattr__(self, name, value, /)\n",
      " |      Implement setattr(self, name, value).\n",
      " |  \n",
      " |  ----------------------------------------------------------------------\n",
      " |  Data and other attributes defined here:\n",
      " |  \n",
      " |  __hash__ = None\n",
      " |  \n",
      " |  ----------------------------------------------------------------------\n",
      " |  Methods inherited from PyIUnknown:\n",
      " |  \n",
      " |  QueryInterface(...)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Grab the Type Info\n",
    "myTypeInfo = xlCom.GetTypeInfo()\n",
    "\n",
    "# let's get some info about our TypeInfo Object. This will be covered in an additional Video\n",
    "help(myTypeInfo)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Flag\tDescription\n",
    "DISPATCH_METHOD\tThe member is invoked as a method. If a property has the same name, both this and the DISPATCH_PROPERTYGET flag may be set.\n",
    "DISPATCH_PROPERTYGET\tThe member is retrieved as a property or data member.\n",
    "DISPATCH_PROPERTYPUT\tThe member is changed as a property or data member.\n",
    "DISPATCH_PROPERTYPUTREF\tThe member is changed by a reference assignment, rather than a value assignment. This flag is valid only when the property accepts a reference to an object.\n",
    "\n",
    "\n",
    "# Invoke\n",
    "\n",
    "- Win32COM Documentation: http://timgolden.me.uk/pywin32-docs/PyIDispatch__Invoke_meth.html\n",
    "- Microsoft Documentation: https://docs.microsoft.com/en-us/windows/desktop/api/oaidl/nf-oaidl-idispatch-invoke\n",
    "\n",
    "**Syntax**\n",
    "*expression*.Invoke(dispid, lcid , flags , bResultWanted , params, ... )\n",
    "\n",
    "**Return**\n",
    "object\n",
    "\n",
    "**Description**\n",
    "Invokes a DISPID, using the passed arguments.\n",
    "\n",
    "**Parameters**\n",
    "\n",
    "- name: dispid \n",
    "- type: int\n",
    "- desc: The dispid to use. Typically this value will come from PyIDispatch::GetIDsOfNames or from a type library.\n",
    "\n",
    "- name: lcid \n",
    "- type: int\n",
    "- desc: The locale id to use.\n",
    "\n",
    "- name: flags \n",
    "- type: int\n",
    "- desc: The flags for the call. The following flags can be used.\n",
    "\n",
    "- name: bResultWanted \n",
    "- type: int\n",
    "- desc: Indicates if the result of the call should be requested.\n",
    "\n",
    "- name: params\n",
    "- type: object\n",
    "- desc: The parameters to pass.\n",
    "\n",
    "*Return Value\n",
    "If the bResultWanted parameter is False, then the result will be None. Otherwise, the result is determined by the COM object itself (and may still be None)*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# InvokeTypes\n",
    "\n",
    "- Win32COM Documentation: http://timgolden.me.uk/pywin32-docs/PyIDispatch__Invoke_meth.html\n",
    "- Microsoft Documentation: https://docs.microsoft.com/en-us/windows/desktop/api/oaidl/nf-oaidl-idispatch-invoke\n",
    "\n",
    "**Syntax**\n",
    "*expression*.InvokeTypes(dispid, lcid , wFlags , resultTypeDesc , typeDescs , args )\n",
    "\n",
    "**Return**\n",
    "object\n",
    "\n",
    "**Description**\n",
    "Invokes a DISPID, using the passed arguments and type descriptions.\n",
    "\n",
    "**Parameters**\n",
    "\n",
    "- name: dispid \n",
    "- type: int\n",
    "- desc: The dispid to use. Typically this value will come from PyIDispatch::GetIDsOfNames or from a type library.\n",
    "\n",
    "- name: lcid \n",
    "- type: int\n",
    "- desc: The locale id to use.\n",
    "\n",
    "- name: wflags \n",
    "- type: int\n",
    "- desc: The flags for the call. The following flags can be used.\n",
    "\n",
    "- name: resultTypeDesc \n",
    "- type: tuple\n",
    "- desc: A tuple describing the type of the result.\n",
    "\n",
    "- name: typeDescs\n",
    "- type: tuple\n",
    "- desc: A sequence of tuples describing the types of the parameters for the function. See the comments for more information.\n",
    "\n",
    "- name: args\n",
    "- type: object\n",
    "- desc: The arguments to be passed.\n",
    "\n",
    "**Comments**\n",
    "The Microsoft documentation for IDispatch should be used for all params except 'resultTypeDesc' and 'typeDescs'. \n",
    "\n",
    "- `resultTypeDesc` - describes the return value of the function, and is a tuple of (type_id, flags). \n",
    "- `typeDescs` - describes the type of each parameters, and is a list of the same (type_id, flags) tuple.\t\n",
    "\n",
    "- item: type_id\t\n",
    "- desc: A valid \"variant type\" constant (eg, VT_I4 | VT_ARRAY, VT_DATE, etc - see VARIANT at MSDN).\n",
    "\n",
    "- item: flags\t\n",
    "- desc: One of the PARAMFLAG constants (eg, PARAMFLAG_FIN, PARAMFLAG_FOUT etc - see PARAMFLAG at MSDN)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 216,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(24, 1)\n"
     ]
    }
   ],
   "source": [
    "# We saw that we can use the GetIDsOfNames method to return the DISPID of any given property or method.\n",
    "# so let's grab the DISPID of the Undo method.\n",
    "\n",
    "# grab the DISPID\n",
    "UndoID = xlCom.GetIDsOfNames('Undo')\n",
    "\n",
    "# define the LCID (The locale id) to use.\n",
    "LCID = 0x0\n",
    "\n",
    "# define the flags for call, in this case we are using a method so we will use DISPATCH_METHOD\n",
    "wFlags = pythoncom.DISPATCH_METHOD\n",
    "\n",
    "# define the resultTypeDesc, this particular method does not return anything (pythoncom.VT_VOID or 24) and \n",
    "# and the parameter passes information from the caller to the callee (pythoncom.PARAMFLAG_FIN or 1).\n",
    "resultTypeDesc = (24, 1) \n",
    "\n",
    "#could also be written as a tuple but I'll save this for later. (pythoncom.VT_VOID, pythoncom.PARAMFLAG_FIN)\n",
    "\n",
    "# there are no arguments to pass through in this example, so we don't need to define their types\n",
    "# so we will pass through an empty tuple.\n",
    "typeDescs = ()\n",
    "\n",
    "# no arguments - SIMPLY A PLACE HOLDER\n",
    "args = ''\n",
    "\n",
    "# Let's put all the pieces together and invoke the method.\n",
    "xlCom.InvokeTypes(UndoID, LCID, wFlags, resultTypeDesc, typeDescs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 230,
   "metadata": {},
   "outputs": [],
   "source": [
    "# We saw that we can use the GetIDsOfNames method to return the DISPID of any given property or method.\n",
    "# so let's grab the DISPID of the Range Property.\n",
    "\n",
    "# grab the DISPID\n",
    "RangeID = xlCom.GetIDsOfNames('Range')\n",
    "\n",
    "# define the LCID (The locale id) to use. We just need to define the LOCALUSER\n",
    "LCID = 0x0\n",
    "\n",
    "# define the flags for call, in this case we are using a property so we will use DISPATCH_PROPERTYGET\n",
    "wFlags = pythoncom.DISPATCH_PROPERTYGET\n",
    "\n",
    "# do we want the results back? False means we get NONE True means we get the Result back if there is one.\n",
    "bResultWanted = False\n",
    "\n",
    "# no parameters to pass - SIMPLY A PLACE HOLDER\n",
    "cell1 = 'A1'\n",
    "cell2 = 'A2'\n",
    "\n",
    "# Let's put all the pieces together and invoke the method.\n",
    "myRangeObject = xlCom.Invoke(UndoID, LCID, wFlags, bResultWanted, param1, param2)\n",
    "myRangeObject"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 233,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(197, 0, 2, (9, 0), ((12, 1), (12, 1)), 'A1', 'A2')\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<PyIDispatch at 0x00000219F6B82A90 with obj at 0x00000219F6B88AB8>"
      ]
     },
     "execution_count": 233,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# grab the DISPID\n",
    "RangeID = xlCom.GetIDsOfNames('Range')\n",
    "\n",
    "\n",
    "# define the LCID (The locale id) to use. We just need to define the LOCALUSER\n",
    "LCID = 0x0\n",
    "\n",
    "\n",
    "# define the flags for call, in this case we are using a property so we will use DISPATCH_PROPERTYGET\n",
    "wFlags = pythoncom.DISPATCH_PROPERTYGET\n",
    "\n",
    "\n",
    "# define the resultTypeDesc, this particular method returns a dispatch object (pythoncom.VT_DISPATCH) and \n",
    "# and the type of the contained field is undefined (pythoncom.VT_EMPTY). \n",
    "resultTypeDesc = (pythoncom.VT_DISPATCH, pythoncom.VT_EMPTY)\n",
    "\n",
    "\n",
    "# define the type descriptions of the arguments we are passing in.\n",
    "# 2 arguments - given by two tuples\n",
    "# first and second argument will be a variant (pythoncom.VT_VARIANT), and \n",
    "# the parameter passes information from the caller to the callee pythoncom.PARAMFLAG_FIN\n",
    "typeDescs = ((pythoncom.VT_VARIANT, pythoncom.PARAMFLAG_FIN), (pythoncom.VT_VARIANT, pythoncom.PARAMFLAG_FIN))\n",
    "\n",
    "# rule of thumb:\n",
    "# (define the data type of the parameter, define how to transfer the parameter)\n",
    "\n",
    "# define our two arguments, the range property takes two arguments cell1 and cell.\n",
    "cell1 = 'A1'\n",
    "cell2 = 'A2' \n",
    "\n",
    "\n",
    "# print everything together.\n",
    "print((RangeID, LCID, wFlags, resultTypeDesc, typeDescs, cell1, cell2))\n",
    "\n",
    "# grab the range object\n",
    "xlCom.InvokeTypes(RangeID, LCID, wFlags, resultTypeDesc, typeDescs, cell1, cell2)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
